package mnet

/*
 * net connection manager.
 */

import (
	"net"
	"sync"
	"sync/atomic"
	"time"
)

const (
	gArraySize = 16
)

// connections map with lock for multi-goroutine.
type connsMap struct {
	connMap map[uint32]IConnection
	mutex   sync.RWMutex
}

func newConnsMap() *connsMap {
	return &connsMap{
		connMap: map[uint32]IConnection{},
	}
}

func (cm *connsMap) find(connId uint32) IConnection {
	cm.mutex.RLock()
	defer cm.mutex.RUnlock()

	if v, ok := cm.connMap[connId]; ok {
		return v
	} else {
		return nil
	}
}
func (cm *connsMap) remove(connId uint32) {
	cm.mutex.Lock()
	defer cm.mutex.Unlock()

	delete(cm.connMap, connId)
}
func (cm *connsMap) add(connId uint32, conn IConnection) {
	cm.mutex.Lock()
	defer cm.mutex.Unlock()

	cm.connMap[connId] = conn
}
func (cm *connsMap) len() int {
	cm.mutex.RLock()
	defer cm.mutex.RUnlock()

	return len(cm.connMap)
}
func (cm *connsMap) stop() {
	cm.mutex.RLock()
	defer cm.mutex.RLock()

	for _, conn := range cm.connMap {
		conn.stop()
	}
}

// connection manager
type mConnManager struct {
	nextId uint32
	arConn [gArraySize]*connsMap
}

func newConnManager() *mConnManager {
	mgr := &mConnManager{
		nextId: 1,
	}
	mgr.init()
	return mgr
}

// init connections.
func (connMgr *mConnManager) init() {
	for i := 0; i < gArraySize; i++ {
		connMgr.arConn[i] = newConnsMap()
	}
}

// create creates connection with session and parser.
func (connMgr *mConnManager) create(net INet, conn *net.TCPConn, session ISession, codec ICodec) IConnection {
	if conn == nil || session == nil || codec == nil {
		return nil
	}
	connId := atomic.AddUint32(&connMgr.nextId, 1)
	c := newConnection(net, conn, connId, session, codec)
	connMgr.arConn[c.GetConnID()%gArraySize].add(connId, c)
	return c
}

// remove removes connection
func (connMgr *mConnManager) remove(conn IConnection) {
	connMgr.arConn[conn.GetConnID()%gArraySize].remove(conn.GetConnID())
}

// find finds Connection
func (connMgr *mConnManager) find(connId uint32) IConnection {
	return connMgr.arConn[connId%gArraySize].find(connId)
}

// len gets length.
func (connMgr *mConnManager) len() int {
	allLen := 0
	for i := 0; i < gArraySize; i++ {
		allLen += connMgr.arConn[i].len()
	}
	return allLen
}

// stop stops all connections.
func (connMgr *mConnManager) stop() {
	for i := 0; i < gArraySize; i++ {
		connMgr.arConn[i].stop()
	}
}

// wait waits all connect to disconnect.
func (connMgr *mConnManager) wait() {
	for connMgr.len() > 0 {
		time.Sleep(100 * time.Millisecond)
	}
	log().Info("[net] All connections exit")
}
